/*  ----------------------------------------------------------------------------
 *  Arello Mobile
 *  Fast 3d Engine
 *  ----------------------------------------------------------------------------
 * Except where otherwise noted, this work is licensed under
 * http://creativecommons.org/licenses/by/3.0
 */

#include "LuaScript.h"
#include "ScriptFactory.h"
#include <resource/Manager.h>

// register script type in scripts factory
registerScriptType("lua", LuaScript);

LuaScript::LuaScript() : _luaState(NULL)
{
	initializeEngine();
}

LuaScript::~LuaScript()
{
	closeEngine();
}

void LuaScript::initializeEngine()
{
	// initialize Lua
	_luaState = lua_open();
	
	// initialize Lua's modules
	luaopen_base(_luaState);
	luaopen_string(_luaState);
	luaopen_table(_luaState);
	luaopen_math(_luaState);
}

void LuaScript::closeEngine()
{
	// close Lua
	lua_close(_luaState);
	_luaState = NULL;
}

bool LuaScript::createFromFile(const char * path)
{
	// try to open file
	AutoPtr<resource::Resource> inputFile = resource::Manager::create_resource(path);
	if(!inputFile)
	{
		closeEngine();
		printf("Unable to read Lua script from file '%s'\n", path);
		return false;
	}
	
	// read data from file
	std::string scriptSource;
	scriptSource.resize(inputFile->get_size());
	inputFile->read(&scriptSource[0], inputFile->get_size());
	
	// load script
	int result = luaL_loadbuffer(_luaState, scriptSource.c_str(), scriptSource.length(), path);
    if(result == 0)
		result = lua_pcall(_luaState, 0, LUA_MULTRET, 0);
	
	// check errors
	if(result != 0)
	{
		printf("%s\n", lua_tostring(_luaState, -1));
		lua_pop(_luaState, 1);
		closeEngine();
		return false;
	}
	
	// all done
	return true;
}

bool LuaScript::createFromString(const char * code)
{
	// load script
	int result = luaL_loadbuffer(_luaState, code, strlen(code), "memory");
    if(result == 0)
		result = lua_pcall(_luaState, 0, LUA_MULTRET, 0);
	
	// check errors
	if(result != 0)
	{
		closeEngine();
		printf("%s\n", lua_tostring(_luaState, -1));
		lua_pop(_luaState, 1);
		return false;
	}
	
	// all done
	return true;
}

bool LuaScript::run()
{
	// check if here is no any compiled script
	if(_luaState == NULL)
		return false;
	
	// call script
	int result = lua_pcall(_luaState, 0, LUA_MULTRET, 0);
	
	// check errors
	if(result != 0)
	{
		printf("%s\n", lua_tostring(_luaState, -1));
		lua_pop(_luaState, 1);
	}
	
	// all done
	return true;
}

bool LuaScript::run(const char * functionName, IScript::ScriptArgument * arguments)
{
	// check if here is no any compiled script
	if(_luaState == NULL)
		return false;
	
	lua_getglobal(_luaState, functionName);
	if(!lua_isfunction(_luaState, -1))
	{
		printf("LUA Error: '%s' is not a function.\n", functionName);
		lua_pop(_luaState, 1);
		return false;
	}

	// sending arguments
	int argument = 0;
	if(arguments != NULL)
	{
		for(; arguments[argument].type != ScriptArgument::ENDOFLIST; argument++)
		{
			switch(arguments[argument].type)
			{
				case ScriptArgument::INTEGER:
					lua_pushinteger(_luaState, arguments[argument].intValue);
					break;
					
				case ScriptArgument::FLOAT:
					lua_pushnumber(_luaState, arguments[argument].floatValue);
					break;
					
				case ScriptArgument::BOOLEAN:
					lua_pushboolean(_luaState, arguments[argument].booleanValue);
					break;
					
				case ScriptArgument::STRING:
					lua_pushstring(_luaState, arguments[argument].stringValue);
					break;
			}
		}
	}
	
	// call function
	int result = lua_pcall(_luaState, argument, LUA_MULTRET, 0);
	
	// check errors
	if(result != 0)
	{
		printf("%s\n", lua_tostring(_luaState, -1));
		lua_pop(_luaState, 1);
	}
	
	// all done
	return true;
}

bool LuaScript::registerFunction(const char * functionName, void * function)
{
	// check if here is no any compiled script
	if(_luaState == NULL)
		return false;
	
	// register function
	lua_register(_luaState, functionName, (int(*)(lua_State*))function);
	
	// all ok
	return true;
}